package com.cskaoyan.javase.thread._5pc._1wait_notify;

import java.util.concurrent.TimeUnit;

/**
 * wait等待 --> notify唤醒
 * notify()方法
 * 使用的总结:
 *      1.必须配合synchronized关键字，让锁对象去调用该方法，否则会抛出异常IllegalMonitorStateException。
 *      2.执行notify()方法的线程会唤醒此锁对象上随机的一个wait()等待线程。
 *          （如果只有一个线程wait，则固定唤醒一个）
 *      3.(重点)执行完毕wait()方法的线程会立刻释放锁对象，但执行完毕notify()方法的线程却不会立刻释放锁对象，而是等待synchronized结构执行完毕后才会释放锁对象。
 *      4.没有被notify()唤醒的wait线程会继续等待，继续处于阻塞状态。
 *
 * @since 10:58
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class TestNotify {
    // 一把同步锁
    private static final Object OBJECT = new Object();
    public static void main(String[] args) {
        // 创建并启动一个线程
        new Thread(() -> {
            System.out.println("A线程已经执行！");
            // sync
            synchronized (OBJECT) {
                System.out.println("A线程执行sync！");
                try {
                    // A线程拿到锁以后会休眠10s中,在它休眠的过程中,B线程会启动,输出B线程已经执行了.但是锁对象B线程拿不到,B线程阻塞了不能继续执行
                    TimeUnit.SECONDS.sleep(10);
                    // 10s休眠到期以后,输出
                    System.out.println("wait before");
                    // wait会释放锁对象,同时阻塞自己
                    OBJECT.wait();
                    // 在B线程休眠10s后,A拿到锁对象,A输出wait after,A线程执行完毕
                    System.out.println("wait after");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        // 主线程休眠3s
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 创建并启动一个线程
        new Thread(() -> {
            System.out.println("B线程已经执行！");
            // sync
            synchronized (OBJECT) {
                // A线程输出wait before并且执行完wait方法后,它会释放锁,因为A线程已经阻塞,那么B现在就会拿到锁,开始执行sync
                System.out.println("B线程执行sync！");
                System.out.println("notify before");
                // 这个方法执行后,会唤醒A线程,但是没有乐可释放锁,所以A线程不能立刻执行,需要等待锁对象
                OBJECT.notify();
                System.out.println("notify after");
                // 下面B线程会休眠10s,10s执行完毕sync,释放锁\
                // B线程执行完毕
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}
